package api

import (
	"fmt"
	"log"
	"os"
	"os/signal"

	common "go-admin/common/middleware"

	"go-admin/common/database"

	"github.com/gin-gonic/gin"
	"github.com/go-admin-team/go-admin-core/config/source/file"
	"github.com/go-admin-team/go-admin-core/sdk"
	"github.com/go-admin-team/go-admin-core/sdk/config"
	"github.com/go-admin-team/go-admin-core/sdk/pkg"
	"github.com/spf13/cobra"
)

// 初始化cobra的数据
var (
	configYml string
	StartCmd  = &cobra.Command{
		Use:          "server",
		Short:        "Start API server",
		Example:      "go-admin server -c config/settings.yml",
		SilenceUsage: true,
		PreRun: func(cmd *cobra.Command, args []string) {
			setup()
		},
		RunE: func(cmd *cobra.Command, args []string) error {
			return run()
		},
	}
)

var AppRouters = make([]func(), 0) // 路由存放的集合

// cobra实现cmd命令的初始化函数
func init() {
	StartCmd.PersistentFlags().StringVarP(&configYml, "config", "c", "config/settings.yml", "Start server with provided configuration file")
}

// 程序启动前加载配置
func setup() {

	//1. 读取配置
	config.Setup(
		file.NewSource(file.WithPath(configYml)), // 读取配置文件
		database.Setup,
	)

	usageStr := `starting api server...`
	log.Println(usageStr)
}

func run() error {
	// 判断当前是否是生产环境
	if config.ApplicationConfig.Mode == pkg.ModeProd.String() {
		gin.SetMode(gin.ReleaseMode)
	}

	ginEngine := initGin() //初始化gin

	// 初始化路由
	for _, f := range AppRouters {
		f()
	}
	// 独立协程启动gin
	go ginEngine.Run(fmt.Sprintf("%s:%d", config.ApplicationConfig.Host, config.ApplicationConfig.Port))

	fmt.Println(pkg.Green("Server run at:"))
	fmt.Printf("-  Local:   http://localhost:%d/ \r\n", config.ApplicationConfig.Port)
	fmt.Println(pkg.Green("Swagger run at:"))
	fmt.Printf("-  Local:   http://localhost:%d/swagger/index.html \r\n", config.ApplicationConfig.Port)
	fmt.Printf("-  Network: http://%s:%d/swagger/index.html \r\n", pkg.GetLocaHonst(), config.ApplicationConfig.Port)
	fmt.Printf("Enter Control + C Shutdown Server \r\n")
	// 等待中断信号以优雅地关闭服务器
	quit := make(chan os.Signal, 1)
	signal.Notify(quit, os.Interrupt) // 监听 ctrl+c
	<-quit                            // 接收到ctrl+c后，后续的代码才会执行到
	fmt.Printf("%s Server done \r\n", pkg.GetCurrentTimeStr())

	return nil
}

// 初始化gin
func initGin() *gin.Engine {

	r := gin.New()           // 初始化gin
	sdk.Runtime.SetEngine(r) // 将该gin对象设置到此，之后可以使用getEngine获取，做到全局唯一gin对象吗，这是go-admin框架的作用

	common.InitMiddleware(r) // 初始化中间件
	return r
}
